package com.aptemo.webetek.server.handlers;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;

import com.aptemo.webetek.client.commands.Action;
import com.aptemo.webetek.client.commands.impl.PlaceBetAction;
import com.aptemo.webetek.client.enums.EnumBetType;
import com.aptemo.webetek.client.enums.EnumEventStatus;
import com.aptemo.webetek.client.exceptions.BackendException;
import com.aptemo.webetek.client.exceptions.WebetekLoginException;
import com.aptemo.webetek.client.exceptions.BackendException.EErrorCodes;
import com.aptemo.webetek.server.data.Bet;
import com.aptemo.webetek.server.data.PMF;
import com.aptemo.webetek.server.data.Pick;
import com.aptemo.webetek.server.data.WebetekUser;
import com.aptemo.webetek.server.datastore.DataStoreAPI;
import com.aptemo.webetek.shared.BetBean;
import com.aptemo.webetek.shared.TeamBean;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.labs.taskqueue.Queue;
import com.google.appengine.api.labs.taskqueue.QueueFactory;
import com.google.appengine.api.labs.taskqueue.TaskOptions;

public class PlaceBetHandler
		extends ActionHandler<PlaceBetAction, BetBean>
{
	// private static final Logger log =
	// Logger.getLogger(PlaceBetHandler.class.getName());

	public PlaceBetHandler(DataStoreAPI service)
	{
		super(service);
	}

	public Class<? extends Action<?>> getActionType()
	{
		return PlaceBetAction.class;
	}

	@Override
	public BetBean invoke(PlaceBetAction action) throws BackendException, WebetekLoginException
	{
		utils.checkLogin();

		String teamId = action.getTeamId();
		if (!utils.isTeamMember(teamId))
		{
			throw new BackendException(EErrorCodes.NOT_TEAM_MEMBER);
		}

		WebetekUser user = utils.getCurrentUser(teamId);
		String userId = user.getSid();

		List<String> skeys = action.getPickKeys();
		if (skeys.size() <= 0)
		{
			throw new BackendException(EErrorCodes.INVALID_NO_OF_PICKS);
		}

		// get payment amount
		double totalAmount = action.getAmount();
		if (totalAmount <= 0.0)
		{
			throw new BackendException(EErrorCodes.STAKE_BALANCE_WRONG);
		}
		if (action.getBetType() == EnumBetType.SINGLE)
		{
			totalAmount = action.getAmount() * skeys.size();
		}

		// check duplicates and date...
		PersistenceManager pm = PMF.get().getPersistenceManager();

		Date hightime = new Date(System.currentTimeMillis() + TIME_FUSE_CONST);

		// UserGroup team = utils.extractTeam(teamId, pm);
		TeamBean team = dataService.getTeam(teamId);

		// check for duplicates...
		if (!team.isAllowDuplicateBets())
		{
			// to check duplicates..
			List<String> tmpEvents = new ArrayList<String>();
			for (String item : skeys)
			{
				Key pickKey = KeyFactory.stringToKey(item);
				String eventId = KeyFactory.keyToString(pickKey.getParent());

				// checking more than 1 pickkey in a list..
				if (tmpEvents.contains(eventId))
				{
					throw new BackendException(EErrorCodes.BET_PLACED_ALREADY);
				}

				tmpEvents.add(eventId);

				ArrayList<String> picks = (ArrayList<String>) dataService.getPicksForEvent(userId, eventId);
				if (picks.size() > 0)
				{
					throw new BackendException(EErrorCodes.BET_PLACED_ALREADY);
				}

				// check date...
				Bet event = pm.getObjectById(Bet.class, pickKey.getParent());
				Date betDate = event.getDate();
				if (betDate.before(hightime))
				{
					throw new BackendException(EErrorCodes.STAKE_DATE_EXCEEDED);
				}
				// check bet status
				if (event.getStatus() != EnumEventStatus.ACTIVE)
				{
					throw new BackendException(EErrorCodes.WRONG_BET_STATUS, "CLOSED");
				}
			}
		}

		// 1. update pick cnt & odds
		Transaction tx = pm.currentTransaction();
		try
		{
			tx.begin();
			updatePickOdds(pm, skeys, action.getAmount());
			tx.commit();
		}
		finally
		{
			if (tx.isActive())
			{
				tx.rollback();
			}
			pm.close();
		}

		// 2. create user bet
		pm = PMF.get().getPersistenceManager();
		Transaction tx2 = pm.currentTransaction();
		try
		{
			BetBean stake = dataService.createBet(userId, teamId, totalAmount, skeys, action.getBetType());

			// send notifications...
			if (team.isNotifications())
			{
				String owner = team.getOwnerEmail();
				String content = "Hi,";
				content += "\r\n\r\nUser: " + user.getEmail() + " (" + team.getName() + ")";
				content += "\r\nPayment total: " + totalAmount;
				// content += "\r\n\r\n";
				int i = 0;
				for (String item : skeys)
				{
					Key pickKey = KeyFactory.stringToKey(item);
					String eventId = KeyFactory.keyToString(pickKey.getParent());

					i++;
					Pick pick = pm.getObjectById(Pick.class, pickKey);
					content += "\r\n\r\n";
					content += i + "......................";

					Bet bet = pm.getObjectById(Bet.class, eventId);
					content += "\r\n    Event Title: " + bet.getTitle();
					content += "\r\n    Event Date: " + bet.getDate();
					content += "\r\n    Your Pick: " + pick.getTitle();
					content += "\r\n    Odds: " + pick.getOdds();
					content += "\r\n    Amount: " + action.getAmount();
				}

				content += "\r\n\r\n";
				content += "Enjoy the game,";
				content += "\r\n---------------------------------------------\r\nWebetek Online (2010) - webetek.online@gmail.com";
				content += "\r\n";
				content += "\r\n" + "http://webetek.appspot.com/#group-" + action.getTeamId() + "";

				// send email seperatelly (quota problems...)
				String subject = "[Webetek] New bet placed on " + team.getName();
				createEmailSenderTask("", owner, "", subject, content);
				createEmailSenderTask("", user.getEmail(), "", subject, content);
			}
			return stake;
		}
		finally
		{
			// if (tx.isActive())
			// {
			// updatePickOdds(pm, skeys, action.getAmount());
			//
			// tx.rollback();
			// }
			pm.close();
		}

	}

	private void updatePickOdds(PersistenceManager pm, List<String> pickKeysStr, double amount)
	{
		for (String item : pickKeysStr)
		{
			Key key = KeyFactory.stringToKey(item);

			// update pick count
			Bet bet = pm.getObjectById(Bet.class, key.getParent());
			// update all affected bet picks...
			int allpicks = bet.getPickedSum() + 1;
			// double weightTotal = bet.getWeightTotal() + amount;

			for (Pick pick : bet.getPicks())
			{
				// check if picked & increment cnt
				if (pick.getId().equals(key))
				{
					pick.incrementPicked();
					// pick.incrementWeight(amount);
				}

				// TODO check if odds is to automatically calculated
				// if ()

				int pickedcount = pick.getPicked();
				// double weight = pick.getWeight();

				if ((pickedcount == 0) /* || (weight == 0) */)
				{
					pick.setOdds(Double.POSITIVE_INFINITY);
					continue;
				}

				// avoid division by zero
				double odds = (allpicks + 0.0) / (pickedcount);
				// double odds = (weightTotal) / (weight);

				DecimalFormat twoDForm = new DecimalFormat("#.##");
				double normodds = Double.valueOf(twoDForm.format(odds));

				pick.setOdds(normodds);
			}

		}
	}

}
